/*
 * player_data.cpp
 *
 *  Created on: Aug 14, 2013
 *      Author: killerg
 */

#include "../server_deps.h"
#include "../server.h"
#include "../client_session.h"
#include "../server_cfg.h"

#include "player_data.h"
#include "occupation_mgr.h"
#include "player_mgr.h"
#include "game_cfg.h"
#include "chat_mgr.h"
#include "mail_mgr.h"

#include <algorithm>

#include "share/sl_msg.pb.h"
#include "share/net_s.h"
#include "share/clock.h"
#include "share/util.h"
#include "share/msg.h"

#include "proto_mgr.h"

bool sprite_t::init(sprite_t* sprite) {
	proto = MAProto(Sprite, sprite->proto->id);
	if (NULL == proto)
		return false;

	this->serial = sprite->serial;
	for (int i = 0; i < SpriteSkillNum; ++i)
		this->skill_level[i] = sprite->skill_level[i];

	return true;
}

bool sprite_t::init(int32_t protoid, int32_t serial) {
	proto = MAProto(Sprite, protoid);
	if (NULL == proto)
		return false;

	this->serial = serial;
	for (int i = 0; i < SpriteSkillNum; ++i)
		skill_level[i] = 1;

	return true;
}

void sprite_t::destroy() {
	proto = NULL;
}

int32_t sprite_t::calc_combat_force() {
	int total_level = 0;
	for (int i=0; i<SpriteSkillNum; ++i) {
		total_level += skill_level[i];
	}
	return total_level * 30;
}

bool sprite_t::init(const Json::Value &json) {
	proto = MAProto(Sprite, json["protoid"].asInt());
	if (NULL == proto)
		return false;

	serial = json["serial"].asInt();
	for (int i = 0; i < SpriteSkillNum; ++i)
		skill_level[i] = json["skill_level"].get(i, 1).asInt();

	return true;
}

bool sprite_t::save(Json::Value &json) {
	json["protoid"] = proto->id;

	json["serial"] = serial;
	for (int i = 0; i < SpriteSkillNum; ++i)
		json["skill_level"].append(skill_level[i]);

	return true;
}

bool pet_t::init(pet_t* pet) {

	this->proto = pet->proto;
	this->serial = pet->serial;
	this->def_att = pet->def_att;

	this->s8 = pet->s8;
	this->s4 = pet->s4;
	this->sd = pet->sd;
	this->ss = pet->ss;

	for (int i = 0; i < EPeAtt4Save_ARRAYSIZE; ++i)
		on_attchg(EPeAtt4Save(i), att(EPeAtt4Save(i)), true);

	for (int i = 0; i < EPeAtt8Save_ARRAYSIZE; ++i)
		on_attchg(EPeAtt8Save(i), att(EPeAtt8Save(i)), true);

	for (int i = 0; i < EPeAttDSave_ARRAYSIZE; ++i)
		on_attchg(EPeAttDSave(i), att(EPeAttDSave(i)), true);

	for (int i = 0; i < EPeAttSSave_ARRAYSIZE; ++i)
		on_attchg(EPeAttSSave(i), att(EPeAttSSave(i)), true);

	return true;
}

bool pet_t::init(int32_t protoid, int32_t serial, const int32_t* def_att,
		int32_t level, const int32_t skill[PetSpecSkillNum]) {
	this->serial = serial;

	return _init_syncatt(protoid, level, def_att, skill);
}

bool pet_t::init(int32_t protoid, int32_t serial, int32_t level) {
	this->serial = serial;

	return _init_syncatt(protoid, level, NULL, NULL);
}

bool pet_t::init(const Json::Value &json) {
	serial = json["serial"].asInt();

	_init_syncatt(json["syncatt"]);

	return NULL != proto;
}
bool pet_t::save(Json::Value &json) {
	json["serial"] = serial;

	_save_syncatt(json["syncatt"]);

	return true;
}
void pet_t::destroy() {
	proto = NULL;
}

void pet_t::fill_opet(cs::OPet* pet) {
	pet->set_serial(this->serial);
	pet->set_proto(this->proto->id);
	_fill_syncatt_init(pet);
}

bool pet_t::need_sync_pet() {
	return _need_sync_syncatt();
}

void pet_t::fill_opetsync(cs::OPetSync* petsync) {
	petsync->set_serial(serial);
	_fill_syncatt_sync(petsync);
}

int32_t pet_t::calc_star_level() {
	int32_t total_aptitude = 0;
	for (int i = 0; i < PetAptitudeNum; ++i)
		total_aptitude += att(EPeAtt4Save(Pe4s_AptitudeBegin + i));

	const tagProtoPetStar* petstar_proto = NULL;
	int starlevel = 0;
	for (; starlevel <= 12; starlevel++) {
		const tagProtoPetStar* tmp_proto = ARProto(PetStar, starlevel);
		if (tmp_proto == NULL
				|| (total_aptitude >= tmp_proto->min_aptitude
						&& total_aptitude <= tmp_proto->max_aptitude)) {
			petstar_proto = tmp_proto;
			break;
		}
	}

	return petstar_proto == NULL ? 0 : petstar_proto->id;
}

bool pet_t::calc_att(bool level_pet) {
	const tagProtoCharacter* char_proto =
			MAProto(Character, s4.get(Pe4s_Character));
	if (NULL == char_proto)
		return false;

	int32_t tmp_att[CanSeePetAttNum] = { 0 };
	for (int i = 0; i < CanSeePetAttNum; ++i)
		tmp_att[i] = s4.get(EPeAtt4Save(Pe4s_AttBegin + i));

	int32_t total_aptitude = 0;
	int32_t aptitude[PetAptitudeNum] = { 0 };
	for (int i = 0; i < PetAptitudeNum; ++i) {
		aptitude[i] = s4.get(EPeAtt4Save(Pe4s_AptitudeBegin + i));
		total_aptitude += aptitude[i];
	}

	auto petstar_proto = ARProto(PetStar, calc_star_level());
	double star_rate = 0.0f;
	if (!level_pet && petstar_proto != NULL)
		star_rate = petstar_proto->att_add / 10000.0f;

	int level = s4.get(Pe4s_Level);

	double factor = 10.0f;
	if (type == ET_WorldBoss) {
		factor = 2.0f;
	}

	if (level_pet) {
		tmp_att[0] = ((aptitude[0] / factor) * level + aptitude[0]) * 6;
		tmp_att[1] = ((aptitude[1] / factor) * level + aptitude[1]) + 50;
		tmp_att[2] = ((aptitude[2] / factor) * level + aptitude[2]);
		tmp_att[3] = ((aptitude[3] / factor) * level + aptitude[3]);
	} else {
		int32_t pctmod[4] = {0,0,0,0};
		int32_t fixmod[4] = {0,0,0,0};

		int32_t fristage = att(Pe4s_FStage);
		for (int i=0; i<=fristage; ++i) {
			int32_t att = pet_t::att(EPeAtt4Save(Pe4s_FSAtt1 + i));
			int32_t mod = pet_t::att(EPeAtt4Save(Pe4s_FSMod1 + i));
			if (att >=0 && att <= 3) {
				fixmod[att] += mod;
			} else if (att >= 4 && att <= 7) {
				pctmod[att - 4] += mod;
			}
		}

		static double quality_factor[] = { 0.0f, 0.05f, 0.10f, 0.15f, 0.20f };
		tmp_att[0] = ((aptitude[0] / factor) * level + aptitude[0]) * 5
				* (1.0 + char_proto->factor[0] + quality_factor[proto->quality] + star_rate + pctmod[0] / 10000.0f) + fixmod[0];

		tmp_att[1] = ((aptitude[1] / factor) * level + aptitude[1])
				* (1.0 + char_proto->factor[1] + quality_factor[proto->quality] + star_rate + pctmod[1] / 10000.0f) + 50 + fixmod[1];

		tmp_att[2] = ((aptitude[2] / factor) * level + aptitude[2])
				* (1.0 + char_proto->factor[2] + quality_factor[proto->quality] + star_rate + pctmod[2] / 10000.0f) + fixmod[2];

		tmp_att[3] = ((aptitude[3] / factor) * level + aptitude[3])
				* (1.0 + char_proto->factor[3] + quality_factor[proto->quality] + star_rate + pctmod[3] / 10000.0f) + fixmod[3];
	}
	for (int i = 0; i < CanSeePetAttNum; ++i)
		att(EPeAtt4Save(Pe4s_AttBegin + i), tmp_att[i]);

	calc_combat_force();

	return true;
}

void pet_t::att(EPeAtt4Save att, int32_t value, bool force_sync) {
	if (!force_sync && s4.get(att) == value)
		return;

	s4.set(att, value);
	on_attchg(att, value);
}
void pet_t::att(EPeAtt8Save att, int64_t value, bool force_sync) {
	if (!force_sync && s8.get(att) == value)
		return;

	s8.set(att, value);
	on_attchg(att, value);
}
void pet_t::att(EPeAttDSave att, double_t value, bool force_sync) {
	if (!force_sync && sd.get(att) == value)
		return;

	sd.set(att, value);
	on_attchg(att, value);
}
void pet_t::att(EPeAttSSave att, const string &value, bool force_sync) {
	if (!force_sync && ss.get(att) == value)
		return;

	ss.set(att, value);
	on_attchg(att, value);
}

void pet_t::on_attchg(EPeAtt8Save att, int64_t value, bool init) {
	if (unit) {
		player_t* player = dynamic_cast<player_t*>(unit);
		if (player) {
			player->image_stamp = g_clock->tt_now;
		}
	}
}
void pet_t::on_attchg(EPeAtt4Save att, int32_t value, bool init) {
	if (unit) {
		player_t* player = dynamic_cast<player_t*>(unit);
		if (player) {
			player->image_stamp = g_clock->tt_now;
		}
	}

	switch (att) {
	case Pe4s_Proto:
		proto = MAProto(Pet, value);
		break;
	case Pe4s_CombatForce: {
		if (!init) {
			auto player = dynamic_cast<player_t*>(unit);
			if (NULL != player && player->is_in_combat(serial)) {
				player->need_calc_total_cf = true;
			}
		}
	}
		break;
	}
}
void pet_t::on_attchg(EPeAttDSave att, double_t value, bool init) {
	if (unit) {
		player_t* player = dynamic_cast<player_t*>(unit);
		if (player) {
			player->image_stamp = g_clock->tt_now;
		}
	}
}
void pet_t::on_attchg(EPeAttSSave att, const string &value, bool init) {
	if (unit) {
		player_t* player = dynamic_cast<player_t*>(unit);
		if (player) {
			player->image_stamp = g_clock->tt_now;
		}
	}
}

void pet_t::_sync_reset() {
	s8.chged.reset();
	s4.chged.reset();
	sd.chged.reset();
	ss.chged.reset();
}

bool pet_t::_need_sync_syncatt() {
	return !(s8.chged.none() && s4.chged.none() && sd.chged.none()
			&& ss.chged.none());
}

void pet_t::calc_combat_force() {
	int32_t combatforce = att(Pe4s_Att1) / 5 + att(Pe4s_Att2) + att(Pe4s_Att3)
			+ att(Pe4s_Att4);

	int skill_level = 0;
	for (int i = 0; i < PetSpecSkillNum; ++i) {
		int32_t skillid = att(EPeAtt4Save(i + Pe4s_SkillSSlotBegin));
		if (skillid < 0)
			continue;
		skill_level += skillid % 10;
	}

	combatforce += skill_level * 15;

	int skill_num = 0;
	for (int i = 0; i < PetNormalSkillNum; ++i) {
		int32_t skillid = att(EPeAtt4Save(i + Pe4s_SkillNSlotBegin));
		if (skillid >= 0)
			++skill_num;
	}
	combatforce += skill_num * 50;

	combatforce += att(Pe4s_FStage) * 200;

	att(Pe4s_CombatForce, combatforce);
}

void pet_t::add_exp(int32_t toadd) {
	int32_t level = att(Pe4s_Level);
	int32_t exp = att(Pe4s_Exp) + toadd;

	const tagProtoPetLevel* level_proto = ARProto(PetLevel, level);
	if (NULL == level_proto)
		return;

	while (level < PetMaxLevel && NULL != level_proto
			&& exp >= level_proto->levelup_exp_need) {
		exp -= level_proto->levelup_exp_need;
		++level;
		level_proto = ARProto(PetLevel, level);
	}

	if (NULL == level_proto)
		--level;
	att(Pe4s_Level, level);
	att(Pe4s_Exp, exp);
	calc_att();
}

bool pet_t::_init_syncatt(int32_t protoid, int32_t level,
		const int32_t* def_att, const int32_t* def_spec_skill) {
	auto pet_proto = MAProto(Pet, protoid);
	if (NULL == pet_proto)
		return false;

	att(Pe4s_Proto, protoid);
	att(Pess_Name, "");
	att(Pe4s_Level, level);
	att(Pe4s_PotentPoint, proto->potent_point);
	att(Pe4s_UsedPotentPoint, 0);
	att(Pe4s_ExtraFamily, -1);
	att(Pe4s_FriVal, 0);
	att(Pe4s_FStage, 0);
	for (int i=0; i<6; ++i) {
		att(EPeAtt4Save(Pe4s_FSAtt1 + i), -1);
		att(EPeAtt4Save(Pe4s_FSMod1 + i), 0);
	}

	const tagProtoPetFriendStage* fristage_proto = ARProto(PetFriendStage, att(Pe4s_FStage));
	if (proto) {
		int idx = rand() % fristage_proto->g_randatts.size();
		att(EPeAtt4Save(Pe4s_FSAtt1 + fristage_proto->id),
				fristage_proto->g_randatts[idx].att);
		att(EPeAtt4Save(Pe4s_FSMod1 + fristage_proto->id),
				random(fristage_proto->g_randatts[idx].min, fristage_proto->g_randatts[idx].max));
	}

	if (proto->character < 0) {
		int32_t index = random(0, g_protomgr->rand_characters_hatch.size() - 1);
		att(Pe4s_Character, g_protomgr->rand_characters_hatch[index]);
	} else {
		att(Pe4s_Character, proto->character);
	}

	// 变异
	if (proto->mutate_family >= 0 && prob(proto->mutate_prob))
		att(Pe4s_ExtraFamily, proto->mutate_family);

	// 最大资质
	for (int i = 0; i < PetAptitudeNum; ++i)
		att(EPeAtt4Save(i + Pe4s_AptitudeBegin), proto->aptitude[i]);

	for (int i = 0; i < PetNormalSkillNum; ++i)
		att(EPeAtt4Save(Pe4s_SkillNSlotBegin + i), -1);

	att(Pe4s_NormalSkillSlotNum, proto->def_normalskill_num);

	if (def_spec_skill != NULL) {
		this->def_att = def_att;

		for (int i = 0; i < PetSpecSkillNum; ++i)
			att(EPeAtt4Save(Pe4s_SkillSSlotBegin + i), def_spec_skill[i]);

		return calc_att(true);
	} else {
		int32_t spec_skill[PetSpecSkillNum];
		memset(spec_skill, -1, sizeof(spec_skill));
		int32_t skill_index = 0;
		for (int i = 0; i < 2; ++i) {
			if (proto->skill[i] < 0)
				continue;

			spec_skill[skill_index++] = proto->skill[i];
		}
		if (proto->skill_set >= 0) {
			const tagProtoSkillSet* skillset_proto =
					MAProto(SkillSet, proto->skill_set);
			if (NULL != skillset_proto) {
				int32_t rand_skill = random(skillset_proto->item);
				if (rand_skill >= 0)
					spec_skill[skill_index++] = rand_skill;
			}
		}

		for (int i = 0; i < PetSpecSkillNum; ++i)
			att(EPeAtt4Save(Pe4s_SkillSSlotBegin + i), spec_skill[i]);

		return calc_att();
	}
}

bool pet_t::_init_syncatt(const Json::Value &json) {
	s8.init(json["n8"]);
	s4.init(json["n4"]);
	sd.init(json["d"]);
	ss.init(json["str"]);

	for (int i = 0; i < EPeAtt4Save_ARRAYSIZE; ++i)
		on_attchg(EPeAtt4Save(i), att(EPeAtt4Save(i)), true);

	for (int i = 0; i < EPeAtt8Save_ARRAYSIZE; ++i)
		on_attchg(EPeAtt8Save(i), att(EPeAtt8Save(i)), true);

	for (int i = 0; i < EPeAttDSave_ARRAYSIZE; ++i)
		on_attchg(EPeAttDSave(i), att(EPeAttDSave(i)), true);

	for (int i = 0; i < EPeAttSSave_ARRAYSIZE; ++i)
		on_attchg(EPeAttSSave(i), att(EPeAttSSave(i)), true);

	int fstage = att(Pe4s_FStage);
	for (int i=fstage+1; i<6; ++i) {
		att(EPeAtt4Save(Pe4s_FSAtt1 + i), -1);
		att(EPeAtt4Save(Pe4s_FSMod1 + i), 0);
	}

	if (fstage == 0 && att(Pe4s_FSAtt1) == 0 && att(Pe4s_FSMod1) == 0) {
		const tagProtoPetFriendStage* fristage_proto = ARProto(PetFriendStage, fstage);
		if (fristage_proto) {
			int idx = rand() % fristage_proto->g_randatts.size();
			att(EPeAtt4Save(Pe4s_FSAtt1 + fstage), fristage_proto->g_randatts[idx].att);
			att(EPeAtt4Save(Pe4s_FSMod1 + fstage), random(fristage_proto->g_randatts[idx].min, fristage_proto->g_randatts[idx].max));
		}
	}

	return true;
}
void pet_t::_save_syncatt(Json::Value &json) {
	s8.save(json["n8"]);
	s4.save(json["n4"]);
	sd.save(json["d"]);
	ss.save(json["str"]);
}

void pet_t::_fill_syncatt_init(cs::OPet* opet) {
	s8.fill_init(opet->add_att_init());
	s4.fill_init(opet->add_att_init());
	sd.fill_init(opet->add_att_init());
	ss.fill_init(opet->add_att_init());
}
void pet_t::_fill_syncatt_sync(cs::OPetSync* spetsync) {
	if (s8.need_sync())
		s8.fill_sync(spetsync->add_att_sync());
	if (s4.need_sync())
		s4.fill_sync(spetsync->add_att_sync());
	if (sd.need_sync())
		sd.fill_sync(spetsync->add_att_sync());
	if (ss.need_sync())
		ss.fill_sync(spetsync->add_att_sync());
}

void unit_t::destroy() {
	pets.for_each([](int32_t serial, pet_t* &pet) {
		pet->destroy();
		safe_del(pet);
	});

	sprites.for_each([](int32_t serial, sprite_t* &sprite) {
		sprite->destroy();
		safe_del(sprite);
	});

	cur_sprite = -1;
	memset(combat, -1, sizeof(combat));
}

bool level_t::init_level(int32_t protoid) {
	const tagProtoLevel* proto = MAProto(Level, protoid);
	if (NULL == proto)
		return false;

	int32_t indexes[3] = { -1, -1, -1 };
	switch (proto->random) {
	case 1: {
		vector<int32_t> vec_indexes;
		for (int i = 0; i < PlayerCombatCSlotNum; ++i)
			if (proto->pet_protoid[i] >= 0)
				vec_indexes.push_back(i);

		if (vec_indexes.size() <= 0)
			return false;

		for (int i = 0; i < PlayerCombatCSlotNum; ++i)
			indexes[i] = vec_indexes[random() % vec_indexes.size()];

		std::sort(indexes, indexes + PlayerCombatCSlotNum);
	}
		break;
	case 2: {
		vector<int32_t> vec_indexes;
		for (int i = 1; i < PlayerCombatCSlotNum; ++i)
			if (proto->pet_protoid[i] >= 0)
				vec_indexes.push_back(i);

		if (vec_indexes.size() <= 0)
			return false;

		for (int i = 1; i < PlayerCombatCSlotNum; ++i)
			indexes[i] = vec_indexes[random() % vec_indexes.size()];

		indexes[0] = 0;

		std::sort(indexes, indexes + PlayerCombatCSlotNum);
	}
		break;
	default: {
		for (int i = 0; i < PlayerCombatCSlotNum; ++i)
			indexes[i] = i;
	}
		break;
	}

	int32_t pet_serial = 0;
	for (int i = 0; i < PlayerCombatCSlotNum; ++i) {
		int32_t idx = indexes[i];
		int32_t pet_protoid = proto->pet_protoid[idx];
		if (pet_protoid < 0) {
			combat[i] = -1;
			continue;
		}

		auto pet = new pet_t;
		if (!pet->init(pet_protoid, pet_serial++, proto->pet_def_att[idx],
				proto->level, proto->pet_skill[idx])) {
			pet->destroy();
			delete pet;
			return false;
		}
		pets[pet->serial] = pet;
		combat[i] = pet->serial;
	}

	cur_sprite = -1;
	if (ESpriteQuality_IsValid(proto->sprite_quality)) {
	    const tagProtoSpriteQuality* quality_proto = ARProto(SpriteQuality, proto->sprite_quality);
	    if (NULL != quality_proto) {
	    	int32_t protoid = quality_proto->sprite_protos[rand()%quality_proto->sprite_protos.size()];
	    	if (protoid >= 0) {
				auto sprite = new sprite_t;
				if (!sprite->init(protoid, 0)) {
					sprite->destroy();
					safe_del(sprite);
					return false;
				}

				for (int j = 0; j < SpriteSkillNum; ++j)
					sprite->skill_level[j] = proto->sprite_level;

				sprites[sprite->serial] = sprite;
				cur_sprite = sprite->serial;
	    	}
	    }
	}

	return true;
}

bool level_t::init_worldboss(int32_t protoid, int32_t level) {
	const tagProtoLevel* proto = MAProto(Level, protoid);
	if (NULL == proto)
		return false;

	int32_t pet_serial = 0;
	for (int i = 0; i < PlayerCombatCSlotNum; ++i) {
		int32_t pet_protoid = proto->pet_protoid[i];
		if (pet_protoid < 0) {
			combat[i] = -1;
			continue;
		}

		auto pet = new pet_t;
		pet->type = pet_t::ET_WorldBoss;
		if (!pet->init(pet_protoid, pet_serial++, proto->pet_def_att[i],
				level, proto->pet_skill[i])) {
			pet->destroy();
			delete pet;
			return false;
		}
		pets[pet->serial] = pet;
		combat[i] = pet->serial;
	}

	cur_sprite = -1;
	return true;
}

void level_t::destroy() {
	unit_t::destroy();
}

bool level_t::init_player(pet_t* pet) {
	if (pets.size())
		return false;

	pets[pet->serial] = pet;
	combat[0] = pet->serial;

	return true;
}

void level_t::destroy_player() {
	pets.clear();
	combat[0] = -1;
	destroy();
}


bool item_t::init(int32_t serial, int32_t protoid, int32_t num) {
	this->serial = serial;
	this->proto = MAProto(Item, protoid);
	this->num = num;

	return NULL != proto;
}

bool item_t::init(const Json::Value &json) {
	serial = json["serial"].asInt();
	proto = MAProto(Item, json["proto"].asInt());
	num = json["num"].asInt();

	return NULL != proto;
}

void item_t::save(Json::Value &json) {
	json["serial"] = serial;
	json["proto"] = proto->id;
	json["num"] = num;
}

equip_t::equip_t() {
	equip_level = 0;
	enhance_num = 0;
	for (int i = 0; i < CanSeePetAttNum; ++i)
		star_num[i] = g_game_cfg->EquipStarInit;
	chg = false;
}

bool player_t::init_image(const Json::Value &json) {
	id = json["id"].asInt64() + json["serverid"].asInt64() * co::MaxAccountNum;
	image_stamp = json["image_stamp"].asInt();

	n.logoff_date = json["logoff_date"].asInt64();
	n.valid_item_serial = json["valid_item_serial"].asInt();
	n.valid_pet_serial = json["valid_pet_serial"].asInt();
	n.valid_sprite_serial = json["valid_sprite_serial"].asInt();

	_init_equip_bar(json["equip_bar"]);
	_init_syncatt(json["syncatt"]);

	const Json::Value &json_pets = json["pets"];
	for (auto itr = json_pets.begin(); itr != json_pets.end(); ++itr) {
		if (!is_in_combat((*itr)["serial"].asInt()))
			continue;

		auto pet_data = new pet_t;
		if (!pet_data->init(*itr)) {
			pet_data->destroy();
			delete pet_data;
			continue;
		}

		add_pet(pet_data, true);
	}

	const Json::Value &json_sprites = json["sprites"];
	for (int i = 0; i < int(json_sprites.size()); ++i) {
		const Json::Value &sprite_json = json_sprites.get(i, -1);
		if (cur_sprite != sprite_json["serial"].asInt())
			continue;

		auto sprite_data = new sprite_t;
		sprite_data->init(sprite_json);
		add_sprite(sprite_data, true);
	}

	return true;
}

bool player_t::init(const int64_t playerid, const string &name,
		const EPlayerSex &sex, const string &account) {
	if (!_init_syncatt())
		return false;

	create = g_clock->tt_now;
	id = playerid;
	this->account = account;

	att(Plss_Name, name);
	att(Pl4s_PlayerSex, sex);

	calc_equip_mod();

	return true;
}
bool player_t::init(const Json::Value &json) {
	if (!_init_syncatt())
		return false;

	create = json["create"].asInt64();
	id = json["id"].asInt64() + json["serverid"].asInt64() * co::MaxAccountNum;
	account = json.get("account", "").asCString();
	image_stamp = json.get("image_stamp", -1).asInt();

	n.logoff_date = json["logoff_date"].asInt64();
	n.week_refresh_time = json["week_refresh_time"].asInt64();
	n.code = json["code"].asInt();
	n.valid_item_serial = json["valid_item_serial"].asInt();
	n.valid_pet_serial = json["valid_pet_serial"].asInt();
	n.valid_sprite_serial = json["valid_sprite_serial"].asInt();
	n.code_trytime = json.get("code_trytime", 0).asInt();
	n.code_trytime = json.get("code_trytime", 0).asInt();
	n.forbid = json.get("forbid", 0).asInt();
	n.compensate_gain_stamp = json.get("compensate_gain_stamp", -1).asInt();

	if (n.code > 10 || n.code < 0) {
		n.code = 0;
	}

	p = n;

	_init_bag(json["bag"]);
	_init_equip_bar(json["equip_bar"]);
	_init_syncatt(json["syncatt"]);
	_init_bosslevel(json["bosslevel_progress"]);
	_init_petseen(json["petseen"]);
	_init_pethas(json["pethas"]);
	_init_skillhas(json["skillhas"]);
	_init_levelhas(json["levelhas"]);
	_init_friends(json["friends"]);
	_init_arenalogs(json["arenalog"]);
	_init_alives(json["alives"]);
	_init_train(json["train"]);

	const Json::Value &json_pets = json["pets"];
	for (auto itr = json_pets.begin(); itr != json_pets.end(); ++itr) {
		auto pet_data = new pet_t;
		if (!pet_data->init(*itr)) {
			pet_data->destroy();
			delete pet_data;
			continue;
		}
		add_pet(pet_data, true);
	}

	const Json::Value &json_sprites = json["sprites"];
	for (int i = 0; i < int(json_sprites.size()); ++i) {
		auto sprite_data = new sprite_t;
		if (sprite_data->init(json_sprites.get(i, -1))) {
			add_sprite(sprite_data, true);
		} else {
			sprite_data->destroy();
			safe_del(sprite_data);
		}

	}

	if (att(Pl4s_FormLevel) == 0) {
		att(Pl4s_FormLevel, 1);
	}

	return true;
}

void player_t::save(Json::Value &json) {
	json["objtype"] = "player";
	json["serverid"] = g_cfg->ServerId;
	json["create"] = Json::Int64(create);
	json["image_stamp"] = Json::Int64(image_stamp);

	json["id"] = (long long) id % co::MaxAccountNum;
	json["account"] = account;

	json["code"] = n.code;
	json["logoff_date"] = Json::Value::Int64(n.logoff_date);
	json["week_refresh_time"] = Json::Value::Int64(n.week_refresh_time);
	json["valid_item_serial"] = n.valid_item_serial;
	json["valid_pet_serial"] = n.valid_pet_serial;
	json["valid_sprite_serial"] = n.valid_sprite_serial;
	json["code_trytime"] = Json::Int64(n.code_trytime);
	json["forbid"] = n.forbid;
	json["compensate_gain_stamp"] = n.compensate_gain_stamp;

	for (auto itr = pets.begin(); itr != pets.end(); ++itr) {
		Json::Value json_pet;
		itr->second->save(json_pet);
		json["pets"].append(json_pet);
	}

	for (auto itr = sprites.begin(); itr != sprites.end(); ++itr) {
		Json::Value json_sprite;
		itr->second->save(json_sprite);
		json["sprites"].append(json_sprite);
	}

	_save_bag(json["bag"]);
	_save_equip_bar(json["equip_bar"]);
	_save_syncatt(json["syncatt"]);
	_save_bosslevel(json["bosslevel_progress"]);
	_save_petseen(json["petseen"]);
	_save_pethas(json["pethas"]);
	_save_skillhas(json["skillhas"]);
	_save_levelhas(json["levelhas"]);
	_save_friends(json["friends"]);
	_save_arenalogs(json["arenalog"]);
	_save_alives(json["alives"]);
	_save_train(json["train"]);
}
void player_t::destroy() {
	_destroy_bag();
	_destroy_bosslevel();
	_destroy_petseen();
	_destroy_pethas();
	_destroy_skillhas();
	_destroy_levelhas();
	_destroy_friends();
	_destroy_arenalogs();

	unit_t::destroy();
}

bool player_t::add_save_queue(bool chg) {
	if (chg)
		chg_stamp = g_clock->tt_now;

	if (queuing)
		return false;

	queuing = true;
	g_playermgr->save_queue.push(shared_from_this());

	return true;
}

void player_t::post_save_db(function<void(db_error_t)> cb) {
	if (!g_dbf->ok()) {
		if (cb)
			cb(DBE_TryMoreLater);
		return;
	}
	Json::Value json;
	save(json);
	++saving_count;
	auto player = shared_from_this();
	time_t sav_stamp = g_clock->tt_now;
	++g_playermgr->saving;
	bool res = g_dbf->set(
			fmt_cstr("player:%d:%d", id / co::MaxAccountNum,
					id % co::MaxAccountNum), json,
			[player, cb, sav_stamp](db_error_t err) {
				--g_playermgr->saving;
				--player->saving_count;
				if (err == DBE_Success)
				player->sav_stamp = sav_stamp;
				cb(err);
			});
	if (!res) {
		--saving_count;
		cb(DBE_TryMoreLater);
	}
}

void player_t::update() {
	int vipfinish = att(Pl4s_VipFinishDate);
	if (vipfinish >= 0 && g_clock->tt_now >= vipfinish) {
		att(Pl4s_VipLevel, 0);
		att(Pl4s_VipFinishDate, -1);
	}

	int32_t next_inc = att(Pl4s_PowerIncTime);
	int32_t power = att(Pl4s_Power);

	int32_t maxpower = g_game_cfg->MaxPower;
	const tagProtoMedal* medal_proto = ARProto(Medal, att(Pl4s_Medal));
	if (medal_proto != NULL)
		maxpower += medal_proto->maxpower_add;

	if (power < maxpower) {
		static int32_t unit_sec = g_game_cfg->PowerIncMinute * 60;
		if (next_inc < 0) {
			next_inc = g_clock->tt_now + unit_sec;
			att(Pl4s_PowerIncTime, next_inc);
		} else {
			if (g_clock->tt_now >= next_inc) {
				int32_t ex_sec = g_clock->tt_now - next_inc;
				power += ex_sec / unit_sec + 1;
				next_inc = g_clock->tt_now + unit_sec - ex_sec % unit_sec;

				if (power >= maxpower) {
					power = maxpower;
					next_inc = -1;
				}

				att(Pl4s_Power, power);
				att(Pl4s_PowerIncTime, next_inc);
			}
		}
	}

	if (need_calc_cf) {
		calc_combat_force();
		need_calc_cf = false;
		need_calc_total_cf = true;
	}

	if (need_calc_total_cf) {
		calc_total_combat_force();
		need_calc_total_cf = false;
	}

	if (alivecnt_chg) {
		alivecnt_chg = false;

		for (auto itr = g_protomgr->_arrProtoAlive.begin();
				itr != g_protomgr->_arrProtoAlive.end(); ++itr) {

			auto proto = &(*itr);
			if (!EAliveType_IsValid(proto->type))
				continue;

			if (alives.count(proto->id) > 0)
				continue;

			auto slot = EPlAtt4Save(proto->type + Pl4s_AliveCntBegin);
			if (att(slot) < proto->count)
				continue;

			att(Pl4s_AliveNum, att(Pl4s_AliveNum) + proto->alive_count);
			alives.insert(proto->id);
		}
	}

	if (g_clock->yday_jump) {
		_on_day_chg();
	}

    if (g_clock->tt_now >= n.week_refresh_time) {
        _on_week_chg();
        while (n.week_refresh_time <= g_clock->tt_now) {
            n.week_refresh_time += 60 * 60 * 24 * 7;
        }
    }

	// send modify
	if (need_sync_player() && NULL != sess) {
		CREATE_CS_SMSG(SPlayerSync, playersync);
		fill_splayersync(playersync);
		sess->send_msg(___msg);
	}
	if (!queuing) {
		if (memcmp(&n, &p, sizeof(n))) {
			add_save_queue(true);
		}
	}
	p = n;
}

void player_t::on_logon(client_session_t* sess) {
	this->sess = sess;
	this->sess->stat.logon = g_clock->tt_now;

	tm* tt_logoff = localtime(&n.logoff_date);
	if (tt_logoff->tm_yday != g_clock->tm_now.tm_yday)
		_on_day_chg();

	if (n.week_refresh_time <= 0) {
		tm tmend = *tt_logoff;
	    int end = g_game_cfg->WeekAccRefreshTime[0];
	    int start = tmend.tm_wday;
	    int diff = end - start;
	    if (diff < 0)
	        diff += 7;

	    tmend.tm_mday += diff;
	    tmend.tm_hour = g_game_cfg->WeekAccRefreshTime[1];
	    tmend.tm_min = g_game_cfg->WeekAccRefreshTime[2];
	    tmend.tm_sec = 0;
	    time_t ttend = mktime(&tmend);
	    n.week_refresh_time = ttend;
	}

	for (int i=0; i<MaxTrainRoomNum; ++i) {
		if (_train[i].petid < 0)
			continue;

		if (pets.count(_train[i].petid) == 0) {
			_train[i].petid = -1;
			_train[i].petlvl = -1;
			_train[i].finish = -1;
			_train_mods.insert(i);
		}
	}

	att(Pl4n_SystemMailNew, -1);
	att(Pl4n_PersonMailNew, -1);
	att(Pl4n_PlayerRank, -1);
	att(Pl4n_PetRank, -1);
	att(Pl4n_LuckyStarIndex, -1);
	att(Pl4n_MoneyGetFinish, g_svr.server_begin + 60 * 60 * 24 * g_game_cfg->MoneyGetDay);

	g_occupation->on_logon(this);
	int chn = g_chat->add(id, sess);
	att(Pl4n_Channel, chn);

	auto player = shared_from_this();
	g_chat->send_log(player);
	g_mail->box_load(id,
			[player](db_error_t err, mailbox_t* box) {
				switch (err) {
					case DBE_Success:
						if (NULL == box)
							return;

						player->att(Pl4n_SystemMailNew, box->new_system);
						player->att(Pl4n_PersonMailNew, box->new_person);
						box->player_online = true;
						if (g_game_cfg->CompensateFinish > 0 && g_game_cfg->CompensatePlayerCreateStamp > 0 && g_game_cfg->CompensateReward >= 0) {
							if (g_clock->tt_now < g_game_cfg->CompensateFinish) {
								if (player->create < g_game_cfg->CompensatePlayerCreateStamp && player->n.compensate_gain_stamp < g_game_cfg->CompensatePlayerCreateStamp) {
									g_mail->send_system(player->id,
											EMT_ChargeReward,
											"",
											g_game_cfg->CompensateReward,
											1.0,
											[player](bool ok){
												if (ok) {
													player->n.compensate_gain_stamp = g_clock->tt_now;
												}
											});
								}

							}
						}

						break;
					case DBE_NoExist:
						g_mail->box_create(player->id, player->account, player->att(Plss_Name), [player](db_error_t err) {
									switch (err) {
										case DBE_Success:
										case DBE_Exist:
										g_mail->box_load(player->id, [player](db_error_t err, mailbox_t* box) {
													if (DBE_Success != err)
														return;

													if (NULL == box)
														return;

													player->att(Pl4n_SystemMailNew, box->new_system);
													player->att(Pl4n_PersonMailNew, box->new_person);
													box->player_online = true;
												});
										break;
										default:
										break;
									}

								});
						break;
					default:
						break;
				}
			});
}

void player_t::on_logoff() {
	this->sess->stat.logoff = g_clock->tt_now;
	n.logoff_date = g_clock->tt_now;
	this->sess = NULL;

	att(Pl4n_SystemMailNew, -1);
	att(Pl4n_PersonMailNew, -1);
	g_chat->remove(att(Pl4n_Channel), id);
	att(Pl4n_Channel, -1);

	reset_sync();
	auto player = shared_from_this();
	post_save_db([player](db_error_t err) {
		if (DBE_Success != err)
		player->add_save_queue(true);
	});

	g_mail->box_load(id, [player](db_error_t err, mailbox_t* box) {
		if (box)
		box->player_online = false;
	});
}

void player_t::_on_week_chg() {
	att(Pl4s_WeekAccPayFee, 0);
	att(Pl4s_WeekAccPayRewardId, -1);

	for (int i = 0; i < WeeklyCntTypeNum; ++i)
		att(EPlAtt4Save(Pl4s_WeeklyCntBegin + i), 0);

	att(Pl4s_WeeklyRewardMask, 0);
}

void player_t::_on_day_chg() {
	int logoff_day = (n.logoff_date + 8 * 60 * 60) / (60 * 60 * 24);
	int today = (g_clock->tt_now + 8 * 60 * 60) / (60 * 60 * 24);
	switch (today - logoff_day) {
	case 0:
		break;
	case 1:
		att(Pl4s_ContinueOnlineDayNum, att(Pl4s_ContinueOnlineDayNum) + 1);
		break;
	default:
		att(Pl4s_ContinueOnlineDayNum, 1);
		break;
	}

	att(Pl4s_DayAccPayFee, 0);
	att(Pl4s_DayAccPayRewardId, -1);

	att(Pl4s_SpriteExplNumMoney, g_game_cfg->SpriteExplMaxNumMoney);
	att(Pl4s_ArenaCombatRemainNum, g_game_cfg->ArenaCombatNumPerDay);
	att(Pl4s_OccupyRemainNum, g_game_cfg->MaxOccupyNum);

	_boss_level_progress.for_each(
			[this](int32_t bosslevel_id, boss_level_t* &bosslevel) {
				bosslevel->cur_pass = 0;
				bosslevel->reset_count = 0;
				_boss_level_chg.insert(bosslevel_id);
			});

	for (int i = 0; i < EAliveType_ARRAYSIZE; ++i) {
		auto slot = EPlAtt4Save(Pl4s_AliveCntBegin + i);
		att(slot, 0);
	}
	att(Pl4s_AliveNum, 0);
	att(Pl4s_AliveRewardNum, 0);

	att(Pl4s_DailyGiftGet, 0);
	att(Pl4s_DailyVipGiftGet, 0);

	att(Pl4s_LuckyStarNum, 0);

	att(Pl4s_BuyArenaCombatNumNum, 0);
	att(Pl4s_BuyOccupationNumNum, 0);
	att(Pl4s_FriendCombatNum, 0);

	att(Pl4s_DailyPowerMask, 0);

	att(Pl8s_GiftFromMe1, -1);
	att(Pl8s_GiftFromMe2, -1);
	att(Pl8s_GiftFromMe3, -1);

	att(Pl8s_GiftToMe1, -1);
	att(Pl8s_GiftToMe2, -1);
	att(Pl8s_GiftToMe3, -1);
	att(Pl4s_VentureBossResetNum, 0);
	att(Pl4s_CurFloor, 0);
	att(Pl4s_FloorResetNum, 0);
	att(Pl4s_FriFeedNum, 0);
	att(Pl4s_PointRankCombatTimes, 10);

	alives.clear();

	friends_combated.clear();
	friends_combated_added.clear();
	friends_combated_clear = true;

	n.code_trytime = 0;
}

bool player_t::add_sprite(sprite_t* added, bool init) {
	if (sprites.count(added->serial) > 0)
		return false;

	sprites[added->serial] = added;
	if (!init) {
		sprite_chg.insert(added->serial);
		sprite_remove.erase(added->serial);
	}

	return true;
}
sprite_t* player_t::remove_sprite(int32_t serial) {
	if (sprites.count(serial) <= 0)
		return NULL;

	auto ret = sprites[serial];
	sprites.erase(serial);

	sprite_chg.erase(serial);
	sprite_remove.insert(serial);

	return ret;
}

void player_t::_fill_sprite_init(cs::OPlayer* player) {
	sprites.for_each([&player](int32_t serial, sprite_t* &sprite) {
		auto init = player->add_sprite_init();
		init->set_serial(serial);
		init->set_proto(sprite->proto->id);
		for (int i=0; i<SpriteSkillNum; ++i)
		init->add_skill_level(sprite->skill_level[i]);
	});
}
bool player_t::_need_sync_sprite() {
	return !sprite_chg.empty() || !sprite_remove.empty();
}
void player_t::_fill_sprite_sync(cs::SPlayerSync* playersync) {
	for (auto itr = sprite_chg.begin(); itr != sprite_chg.end(); ++itr) {
		auto sprite = sprites[*itr];
		if (NULL == sprite)
			continue;

		auto chg = playersync->add_sprite_chg();
		chg->set_serial(*itr);
		chg->set_proto(sprite->proto->id);


		for (int i = 0; i < SpriteSkillNum; ++i)
			chg->add_skill_level(sprite->skill_level[i]);
	}
	sprite_chg.clear();

	for (auto itr = sprite_remove.begin(); itr != sprite_remove.end(); ++itr)
		playersync->add_sprite_remove(*itr);
	sprite_remove.clear();
}

bool player_t::add_pet(pet_t* pet, bool init) {
	if (pets.count(pet->serial) > 0)
		return false;

	pets[pet->serial] = pet;
	pet->unit = this;

	if (!init) {
		pet_add.insert(pet->serial);
		pet_remove.erase(pet->serial);

		if (pet->proto->cardid >= 0 && pethas.count(pet->proto->cardid) <= 0) {
			pethas.insert(pet->proto->cardid);
			pethas_added.push_back(pet->proto->cardid);
		}

	    for (int i=0; i<PetSpecSkillNum; ++i) {
	        int32_t skillid = pet->att(EPeAtt4Save(Pe4s_SkillSSlotBegin + i));
	        if (skillid < 0)
	            continue;

	        const tagProtoSkill* skill_proto = MAProto(Skill, skillid);
	        if (NULL == skill_proto)
	            continue;

	        if (skillhas.count(skill_proto->id / 10) <= 0) {
	        	skillhas.insert(skill_proto->id / 10);
	        	skillhas_added.push_back(skill_proto->id / 10);
	        }
	    }

	}

	return true;
}
pet_t* player_t::remove_pet(int32_t serial) {
	if (pets.count(serial) <= 0)
		return NULL;

	auto ret = pets[serial];
	pets.erase(serial);
	ret->unit = NULL;

	pet_remove.insert(serial);
	pet_add.erase(serial);

	return ret;
}

pet_t* player_t::find_pet(int32_t serial) {
	return pets.get(serial);
}

void player_t::_fill_pet_init(cs::OPlayer* player) {
	for (auto itr = pets.begin(); itr != pets.end(); ++itr)
		itr->second->fill_opet(player->add_pet_init());
}

bool player_t::_need_sync_pet() {
	for (auto itr = pets.begin(); itr != pets.end(); ++itr) {
		if (itr->second->need_sync_pet())
			return true;
	}
	return !pet_add.empty() || !pet_remove.empty();
}

void player_t::_fill_pet_sync(cs::SPlayerSync* playersync) {
	for (auto itr = pets.begin(); itr != pets.end(); ++itr) {
		if (pet_add.count(itr->first)) {
			itr->second->fill_opet(playersync->add_pet_added());
		} else if (itr->second->need_sync_pet()) {
			itr->second->fill_opetsync(playersync->add_pet_chg());
		}
	}
	pet_add.clear();

	for (auto itr = pet_remove.begin(); itr != pet_remove.end(); ++itr)
		playersync->add_pet_remove(*itr);
	pet_remove.clear();
}

void player_t::fill_oplayer(cs::OPlayer* player) {
	for (auto itr = pets.begin(); itr != pets.end(); ++itr)
		itr->second->fill_opet(player->add_pet_init());

	_fill_syncatt_init(player);
	_fill_obag_init(player);
	_fill_equip_bar_init(player);
	_fill_bosslevel_init(player);
	_fill_petseen_init(player);
	_fill_pethas_init(player);
	_fill_skillhas_init(player);
	_fill_levelhas_init(player);
	_fill_sprite_init(player);
	_fill_friends_init(player);
	_fill_train_init(player);
}

void player_t::reset_sync() {
	p = n;
	n8.chged.reset();
	n4.chged.reset();
	s8.chged.reset();
	s4.chged.reset();
	ss.chged.reset();
	sd.chged.reset();
	bag_mod.clear();

	for (int i = 0; i < PlayerEquipSlotNum; ++i)
		equip_bar[i].chg = false;

	for (auto itr : pets) {
		itr.second->_sync_reset();
	}
	pet_add.clear();
	pet_remove.clear();

	_boss_level_chg.clear();
	petseen_added.clear();
	pethas_added.clear();
	skillhas_added.clear();
	levelhas_added.clear();
	sprite_chg.clear();
	sprite_remove.clear();
	friends_chged.clear();
	friends_combated_clear = false;
	_train_mods.clear();
}

bool player_t::need_sync_player() {
	bool nosave_needsync = _need_sync_nosaveatt();
	bool save_needsync =
			   _need_sync_syncatt()
			|| _need_sync_obag()
			|| _need_sync_equip_bar()
			|| _need_sync_pet()
			|| !_boss_level_chg.empty()
			|| !petseen_added.empty()
			|| !pethas_added.empty()
			|| !skillhas_added.empty()
			|| !levelhas_added.empty()
			|| _need_sync_sprite()
			|| !friends_chged.empty()
			|| !friends_combated_added.empty()
			|| friends_combated_clear
			|| !_train_mods.empty();


	if (save_needsync && !queuing) {
		add_save_queue(true);
	}

	return nosave_needsync || save_needsync;
}

void player_t::fill_splayersync(cs::SPlayerSync* splayersync) {
	_fill_syncatt_sync(splayersync);
	_fill_obag_sync(splayersync);
	_fill_equip_bar_sync(splayersync);
	_fill_pet_sync(splayersync);
	_fill_bosslevel_sync(splayersync);
	_fill_petseen_sync(splayersync);
	_fill_pethas_sync(splayersync);
	_fill_skillhas_sync(splayersync);
	_fill_levelhas_sync(splayersync);
	_fill_sprite_sync(splayersync);
	_fill_friends_sync(splayersync);
	_fill_train_sync(splayersync);
}

item_t* player_t::get_bag_item(int32_t serial) {
	return bag.get(serial);
}

int32_t player_t::get_bag_item_protoid_by_type(EItemType type) {
	for (auto itr = bag.begin(); itr != bag.end(); ++itr) {
		if (itr->second->proto->type == type)
			return itr->second->proto->id;
	}
	return -1;
}

void player_t::clear_bag() {
	vector<pair<int32_t, int32_t> > pairs;
	bag.for_each([&pairs](int32_t serial, item_t* &item) {
		pairs.emplace_back(serial, item->num);
	});

	for (auto itr = pairs.begin(); itr != pairs.end(); ++itr)
		del_item_by_serial(itr->first, itr->second);
}

bool player_t::can_add_item(int32_t protoid, int32_t num) {
	if (num <= 0)
		return false;

	const tagProtoItem* item_proto = MAProto(Item, protoid);
	if (NULL == item_proto)
		return false;

	for (auto itr = bag.begin(); itr != bag.end(); ++itr) {
		auto item = itr->second;
		if (item->proto->id != protoid)
			continue;

		int32_t can_put = item->proto->overlap - item->num;
		if (can_put > 0)
			num -= min(can_put, num);

		if (num <= 0)
			break;
	}

	if (num > 0) {
		int32_t remain_slot = att(Pl4s_BagPageNum) * PlayerBagSlotNumPerPage
				- bag.size();
		num -= min(item_proto->overlap * remain_slot, num);
	}

	return num <= 0;
}

int32_t player_t::bag_free_slot() {
	return att(cs::Pl4s_BagPageNum) * PlayerBagSlotNumPerPage - bag.size();
}

void player_t::add_item(int32_t protoid, int32_t num) {
	if (num <= 0)
		return;

	const tagProtoItem* item_proto = MAProto(Item, protoid);
	if (NULL == item_proto)
		return;

	for (auto itr = bag.begin(); itr != bag.end(); ++itr) {
		auto item = itr->second;
		if (item->proto->id != protoid)
			continue;

		int32_t can_put = item->proto->overlap - item->num;
		if (can_put > 0) {
			int32_t amount = min(can_put, num);
			num -= amount;
			item->num += amount;

			bag_mod.insert(item->serial);
		}

		if (num <= 0)
			break;
	}

	if (num > 0) {
		int32_t remain_slot = att(cs::Pl4s_BagPageNum) * PlayerBagSlotNumPerPage
				- bag.size();
		for (int i = 0; i < remain_slot; ++i) {
			int32_t amount = min(item_proto->overlap, num);

			item_t* item = new item_t;
			if (!item->init(this->n.valid_item_serial++, protoid, amount)) {
				delete item;
				item = NULL;
				continue;
			}

			bag[item->serial] = item;
			num -= amount;
			bag_mod.insert(item->serial);

			if (num <= 0)
				break;
		}
	}

	return;
}

bool player_t::can_del_item(int32_t protoid, int32_t num) {
	if (num <= 0)
		return false;

	for (auto itr = bag.begin(); itr != bag.end(); ++itr) {
		auto item = itr->second;
		if (item->proto->id != protoid)
			continue;

		int32_t amount = min(item->num, num);
		num -= amount;

		if (num <= 0)
			break;
	}
	return num <= 0;
}

void player_t::del_item(int32_t protoid, int32_t num) {
	if (num <= 0)
		return;

	for (auto itr = bag.begin(); itr != bag.end();) {
		auto cur_itr = itr++;
		auto item = cur_itr->second;
		if (item->proto->id != protoid)
			continue;

		int32_t serial = item->serial;
		int32_t amount = min(item->num, num);
		num -= amount;
		item->num -= amount;
		if (item->num <= 0) {
			bag.erase(cur_itr);
			delete item;
		}

		bag_mod.insert(serial);

		if (num <= 0)
			break;
	}
}

bool player_t::can_del_item_by_serial(int32_t serial, int32_t num) {
	if (num <= 0)
		return false;

	if (bag.count(serial) <= 0)
		return false;

	auto item = bag[serial];
	return item->num >= num;
}

void player_t::del_item_by_serial(int32_t serial, int32_t num) {
	if (num <= 0)
		return;

	if (bag.count(serial) <= 0)
		return;

	auto item = bag[serial];
	item->num -= min(item->num, num);
	if (item->num <= 0) {
		bag.erase(item->serial);
		delete item;
	}
	bag_mod.insert(serial);
}

void player_t::_init_bag(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	for (auto itr = json.begin(); itr != json.end(); ++itr) {

		const Json::Value &item_json = *itr;
		int32_t protoid = item_json["proto"].asInt();
		const tagProtoItem* item_proto = MAProto(Item, protoid);
		if (NULL == item_proto)
			continue;

		item_t* item = new item_t;
		if (!item->init(item_json)) {
			delete item;
			item = NULL;
			continue;
		}

		bag[item->serial] = item;
	}
}

void player_t::_destroy_bag() {
	for (auto itr = bag.begin(); itr != bag.end(); ++itr)
		delete itr->second;
	bag.clear();
}

void player_t::_save_bag(Json::Value &json) {
	for (auto itr = bag.begin(); itr != bag.end(); ++itr) {
		Json::Value item_json;
		itr->second->save(item_json);
		json.append(item_json);
	}
}

bool player_t::_need_sync_obag() {
	return !bag_mod.empty();
}

void player_t::_fill_obag_init(cs::OPlayer* player) {
	for (auto itr = bag.begin(); itr != bag.end(); ++itr) {
		auto item_init = player->add_item_init();

		item_init->set_serial(itr->second->serial);
		item_init->set_proto(itr->second->proto->id);
		item_init->set_num(itr->second->num);
	}

}

void player_t::_fill_obag_sync(cs::SPlayerSync* playersync) {
	for (auto itr = bag_mod.begin(); itr != bag_mod.end(); ++itr) {
		auto item = bag.get(*itr);
		if (NULL == item) {
			playersync->add_item_remove(*itr);
		} else {
			auto item_chg = playersync->add_item_chg();

			item_chg->set_serial(item->serial);
			item_chg->set_proto(item->proto->id);
			item_chg->set_num(item->num);
		}
	}
	bag_mod.clear();
}

bool player_t::_need_sync_equip_bar() {
	for (int i = 0; i < PlayerEquipSlotNum; ++i)
		if (equip_bar[i].chg)
			return true;

	return false;
}

void player_t::_init_equip_bar(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	int32_t num = 0;
	for (auto itr = json.begin(); itr != json.end(); ++itr) {
		const Json::Value &equip_json = *itr;

		equip_bar[num].equip_level = equip_json["level"].asInt();
		equip_bar[num].enhance_num = equip_json["enhance"].asInt();
		const Json::Value &star_json = equip_json["star"];
		if (star_json.isArray())
			for (int i = 0; i < CanSeePetAttNum; ++i)
				equip_bar[num].star_num[i] = star_json.get(i, 0).asInt();

		++num;
	}
}

void player_t::_save_equip_bar(Json::Value &json) {
	for (int i = 0; i < PlayerEquipSlotNum; ++i) {
		Json::Value equip_json;
		equip_json["level"] = equip_bar[i].equip_level;
		equip_json["enhance"] = equip_bar[i].enhance_num;

		Json::Value &star_json = equip_json["star"];
		for (int j = 0; j < CanSeePetAttNum; ++j)
			star_json.append(equip_bar[i].star_num[j]);
		;

		json.append(equip_json);
	}
}

void player_t::_fill_equip_bar_init(cs::OPlayer* player) {
	for (int i = 0; i < PlayerEquipSlotNum; ++i) {
		auto equip_init = player->add_equip_bar_init();

		equip_init->set_pos(i);
		equip_init->set_equip_level(equip_bar[i].equip_level);
		equip_init->set_enhance_num(equip_bar[i].enhance_num);

		for (int j = 0; j < CanSeePetAttNum; ++j)
			equip_init->add_star_num(equip_bar[i].star_num[j]);
	}
}

void player_t::_fill_equip_bar_sync(cs::SPlayerSync* playersync) {
	for (int i = 0; i < PlayerEquipSlotNum; ++i) {
		if (!equip_bar[i].chg)
			continue;

		equip_bar[i].chg = false;

		auto equip_sync = playersync->add_equip_bar_sync();
		equip_sync->set_pos(i);
		equip_sync->set_equip_level(equip_bar[i].equip_level);
		equip_sync->set_enhance_num(equip_bar[i].enhance_num);

		for (int j = 0; j < CanSeePetAttNum; ++j)
			equip_sync->add_star_num(equip_bar[i].star_num[j]);
	}
}

void player_t::att(EPlAtt4Save att, int32_t value) {
	value = std::min(INT32_MAX, value);
	value = std::max(INT32_MIN, value);

	s4.set(att, value);
	_on_attchg(att, value);
}

void player_t::att(EPlAtt8Save att, int64_t value) {
	value = std::min(INT64_MAX, value);
	value = std::max(INT64_MIN, value);

	s8.set(att, value);
	_on_attchg(att, value);
}

void player_t::att(EPlAttDSave att, double_t value) {
	sd.set(att, value);
	_on_attchg(att, value);
}

void player_t::att(EPlAttSSave att, const string &value) {
	ss.set(att, value);
	_on_attchg(att, value);
}

void player_t::att(EPlAtt8Nosave att, int64_t value) {
	n8.set(att, value);
	_on_attchg(att, value);
}
void player_t::att(EPlAtt4Nosave att, int32_t value) {
	n4.set(att, value);
	_on_attchg(att, value);
}

void player_t::_on_attchg(EPlAtt8Save att, int64_t value, bool init) {

}
void player_t::_on_attchg(EPlAtt4Save att, int32_t value, bool init) {
	switch (att) {
	case Pl4s_CurSprite:
		if (init) {
			cur_sprite = value;
			break;
		} else {
			if (sprites.get(value) != NULL)
				cur_sprite = value;
			else
				cur_sprite = -1;
		}
		break;
	case Pl4s_PlayerSex:
	case Pl4s_BagPageNum:
		break;
	case Pl4s_CombatC1:
		if (!init)
			need_calc_total_cf = true;
		combat[0] = value;
		break;
	case Pl4s_CombatC2:
		if (!init)
			need_calc_total_cf = true;
		combat[1] = value;
		break;
	case Pl4s_CombatC3:
		if (!init)
			need_calc_total_cf = true;
		combat[2] = value;
		break;
	case Pl4s_AliveCnt1Hatch:
	case Pl4s_AliveCnt2ArenaCombat:
	case Pl4s_AliveCnt3FriCombat:
	case Pl4s_AliveCnt4Rear:
	case Pl4s_AliveCnt5Explore:
	case Pl4s_AliveCnt6ExplRent:
	case Pl4s_AliveCnt7RandEgg:
	case Pl4s_AliveCnt8Compose:
		alivecnt_chg = true;
		break;
	case Pl4s_EquipAtt1:
	case Pl4s_EquipAtt2:
	case Pl4s_EquipAtt3:
	case Pl4s_EquipAtt4:
		need_calc_cf = true;
		break;
	case Pl4s_Medal:
		image_stamp = g_clock->tt_now;
		break;
	}
}
void player_t::_on_attchg(EPlAttDSave att, double_t value, bool init) {

}
void player_t::_on_attchg(EPlAttSSave att, const string &value, bool init) {
	switch (att) {
	case Plss_Name:
		break;
	}
}

void player_t::_on_attchg(EPlAtt8Nosave att, int64_t value) {

}
void player_t::_on_attchg(EPlAtt4Nosave att, int32_t value) {

}

bool player_t::_need_sync_syncatt() {
	return !(s8.chged.none() && s4.chged.none() && sd.chged.none()
			&& ss.chged.none());
}

void player_t::_on_att_chg() {
	for (int i = EPlAtt4Save_MIN; i <= EPlAtt4Save_MAX; ++i) {
		auto index = EPlAtt4Save(i);
		_on_attchg(index, att(index), true);
	}

	for (int i = EPlAtt8Save_MIN; i <= EPlAtt8Save_MAX; ++i) {
		auto index = EPlAtt8Save(i);
		_on_attchg(index, att(index), true);
	}

	for (int i = EPlAttDSave_MIN; i <= EPlAttDSave_MAX; ++i) {
		auto index = EPlAttDSave(i);
		_on_attchg(index, att(index), true);
	}

	for (int i = EPlAttSSave_MIN; i <= EPlAttSSave_MAX; i = ++i) {
		auto index = EPlAttSSave(i);
		_on_attchg(index, att(index), true);
	}
}

bool player_t::_init_syncatt() {
	n.week_refresh_time = -1;
	n.valid_item_serial = 0;
	n.valid_pet_serial = 0;
	n.valid_sprite_serial = 0;
	n.logoff_date = g_clock->tt_now;
	for (int i = 0; i < PlayerCombatCSlotNum; ++i)
		combat[i] = -1;

	att(Pl8s_Money, 0);
	att(Pl8s_GameMoney, 0);
	att(Pl4s_BagPageNum, 2);
	att(Plss_Name, "hello");
	att(Pl4s_CurSprite, -1);
	att(Pl4s_Medal, g_game_cfg->PlayerMedalInit);
	att(Pl4s_Power, g_game_cfg->MaxPower);
	att(Pl4s_PowerIncTime, -1);
	att(Pl4s_MaxExplMap, -1);
	att(Pl4s_SpriteAnima, 0);
	att(Pl4s_SpriteStarCur, 0);
	att(Pl4s_SpriteStarA, 1);

	for (int i = 0; i < PlayerCombatSlotNum; ++i)
		att(EPlAtt4Save(Pl4s_CombatBegin + i), -1);

	att(Pl4s_ExplActNo, 0);
	att(Pl4s_ExplMap, -1);
	att(Pl4s_ExplPos, -1);
	att(Pl4s_ExplSeed, -1);

	for (int i = 0; i < PlayerExplItemNum; ++i)
		att(EPlAtt4Save(Pl4s_ExplItemBegin + i), -1);

	for (int i = 0; i < EggHatchIndexNum; ++i)
		att(EPlAtt4Save(Pl4s_HatchEggidBegin + i), -1);

	for (int i = 0; i < EggHatchIndexNum; ++i)
		att(EPlAtt4Save(Pl4s_HatchFinTimeBegin + i), -1);

	att(Pl4n_ArenaIndex, -1);
	att(Pl4s_SpriteExplNumMoney, g_game_cfg->SpriteExplMaxNumMoney);
	att(Pl4s_ArenaCombatRemainNum, g_game_cfg->ArenaCombatNumPerDay);


	att(Pl4s_ArenaRewardStep, 0);
	att(Pl8s_Honor, 0);
	att(Pl8s_Pearl, 0);
	att(Pl4s_HatchSlotNum, 2);
	att(Pl4s_OccupyRemainNum, g_game_cfg->MaxOccupyNum);
	att(Pl4n_SystemMailNew, -1);
	att(Pl4n_PersonMailNew, -1);

	att(Pl4s_TaskCompleted1, 0);  // 任务完成1
	att(Pl4s_TaskCompleted2, 0);  // 任务完成2
	att(Pl4s_TaskCompleted3, 0);  // 任务完成3
	att(Pl4s_TaskPage, 		 0);  // 任务篇章
	att(Pl4s_CntEnhancePet   , 0);
	att(Pl4s_CntReleasePet   , 0);
	att(Pl4s_CntSpriteExplore, 0);
	att(Pl4s_CntSpriteSummon , 0);
	att(Pl4s_CntArenaCombat  , 0);
	att(Pl4s_CntHatchEgg     , 0);
	att(Pl4s_CntPetFeed      , 0);

	att(Pl4s_CardRewarded    , 0);

	att(Pl4s_AliveCnt1Hatch, 0);  // 今日孵化次数
	att(Pl4s_AliveCnt2ArenaCombat, 0);  // 今日竞技场战斗
	att(Pl4s_AliveCnt3FriCombat, 0);  // 今日好友切磋
	att(Pl4s_AliveCnt4Rear, 0);  // 今日爱心培养
	att(Pl4s_AliveCnt5Explore, 0);  // 今日探险
	att(Pl4s_AliveCnt6ExplRent, 0);  // 今日探险租用道具
	att(Pl4s_AliveCnt7RandEgg, 0);  // 今日摇蛋
	att(Pl4s_AliveCnt8Compose, 0);  // 今日合成
	att(Pl4s_AliveNum, 0);  // 今日活跃度

	att(Pl4s_AliveRewardNum, 0);  // 今日奖励

	att(Pl4s_ContinueOnlineDayNum, 1);    // 连续在线天数
	att(Pl4s_DailyGiftGet, 0);    // 每日奖励领取
	att(Pl4s_DailyVipGiftGet, 0);    // 每日奖励领取
	att(Pl4s_VipLevel, 0);    // vip等级
	att(Pl4s_VipFinishDate, -1);    // vip结束时间
	att(Pl4s_MoneyGetCur, 0);    // 当前的天使大方送
	att(Pl4s_LuckyStarNum, 0);    // 幸运星
	att(Pl4s_DayAccPayFee, 0);
	att(Pl4s_DayAccPayRewardId, -1);
	att(Pl4s_WeekAccPayFee, 0);
	att(Pl4s_WeekAccPayRewardId, -1);
	att(Pl4s_BuyArenaCombatNumNum, 0);
	att(Pl4s_BuyOccupationNumNum, 0);
	att(Pl4s_FriendCombatNum, 0);
	att(Pl4s_ChargeTimes, 0);
	att(Pl4s_DailyPowerMask, 0);

	for (int i = 0; i < WeeklyCntTypeNum; ++i)
		att(EPlAtt4Save(Pl4s_WeeklyCntBegin + i), 0);

	att(Pl4s_WeeklyRewardMask, 0);
	att(Pl4s_WatchNum, 1);

	att(Pl8s_GiftFromMe1, -1);
	att(Pl8s_GiftFromMe2, -1);
	att(Pl8s_GiftFromMe3, -1);

	att(Pl8s_GiftToMe1, -1);
	att(Pl8s_GiftToMe2, -1);
	att(Pl8s_GiftToMe3, -1);
	att(Pl4s_NextWorldBossCombatTime, -1);
	att(Pl4s_VentureBossResetNum, 0);

	att(Pl4s_FloorResetNum, 0);
	att(Pl4s_FloorRewardLevel, 0);
	att(Pl4s_CurFloor, 0);
	att(Pl4s_MaxFloor, 0);
	att(Pl4s_VentCurLevel, 0);
	att(Pl4s_FriFeedNum, 0);
	att(Pl4s_FormSeed, -1);
	att(Pl4s_FormLevel, 1);
	att(Pl4s_PointRankCombatTimes, 10);
	att(Pl4s_PointRankCooldown, 0);

	_on_att_chg();

	return true;
}

bool player_t::_init_syncatt(const Json::Value &json) {
	s8.init(json["n8"]);
	s4.init(json["n4"]);
	sd.init(json["d"]);
	ss.init(json["str"]);

	_on_att_chg();

	return true;
}

void player_t::_save_syncatt(Json::Value &json) {
	s8.save(json["n8"]);
	s4.save(json["n4"]);
	sd.save(json["d"]);
	ss.save(json["str"]);
}

void player_t::_fill_syncatt_init(cs::OPlayer* oplayer) {
	s8.fill_init(oplayer->add_att_init());
	s4.fill_init(oplayer->add_att_init());
	sd.fill_init(oplayer->add_att_init());
	ss.fill_init(oplayer->add_att_init());
	n8.fill_init(oplayer->add_att_init());
	n4.fill_init(oplayer->add_att_init());
}
void player_t::_fill_syncatt_sync(cs::SPlayerSync* splayersync) {
	if (s8.need_sync())
		s8.fill_sync(splayersync->add_att_sync());
	if (s4.need_sync())
		s4.fill_sync(splayersync->add_att_sync());
	if (sd.need_sync())
		sd.fill_sync(splayersync->add_att_sync());
	if (ss.need_sync())
		ss.fill_sync(splayersync->add_att_sync());
	if (n8.need_sync())
		n8.fill_sync(splayersync->add_att_sync());
	if (n4.need_sync())
		n4.fill_sync(splayersync->add_att_sync());
}

void player_t::_init_bosslevel(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	for (int i = 0; i < int(json.size()); ++i) {
		const Json::Value &bosslevel_json = json.get(i, -1);
		auto bosslevel = new boss_level_t;

		bosslevel->id = bosslevel_json["id"].asInt();
		bosslevel->cur_pass = bosslevel_json["cur_pass"].asInt();
		bosslevel->haspass = bosslevel_json["haspass"].asBool();
		bosslevel->reset_count = bosslevel_json["reset_count"].asInt();

		_boss_level_progress[bosslevel->id] = bosslevel;
	}
}
void player_t::_save_bosslevel(Json::Value &json) {
	for (auto itr = _boss_level_progress.begin();
			itr != _boss_level_progress.end(); ++itr) {

		Json::Value progress;
		progress["id"] = itr->second->id;
		progress["cur_pass"] = itr->second->cur_pass;
		progress["haspass"] = itr->second->haspass;
		progress["reset_count"] = itr->second->reset_count;

		json.append(progress);
	}
}
void player_t::_destroy_bosslevel() {
	for (auto itr = _boss_level_progress.begin();
			itr != _boss_level_progress.end(); ++itr) {
		delete itr->second;
	}
	_boss_level_progress.clear();
}

void player_t::_fill_bosslevel_init(cs::OPlayer* oplayer) {
	for (auto itr = _boss_level_progress.begin();
			itr != _boss_level_progress.end(); ++itr) {

		auto init = oplayer->add_bosslevel_init();
		init->set_id(itr->second->id);
		init->set_cur_pass(itr->second->cur_pass);
		init->set_haspassed(itr->second->haspass);
		init->set_reset_count(itr->second->reset_count);
	}
}
void player_t::_fill_bosslevel_sync(cs::SPlayerSync* splayersync) {
	for (auto itr = _boss_level_chg.begin(); itr != _boss_level_chg.end();
			++itr) {
		auto bosslevel = _boss_level_progress[*itr];
		auto chg = splayersync->add_bosslevel_chg();
		chg->set_id(bosslevel->id);
		chg->set_cur_pass(bosslevel->cur_pass);
		chg->set_haspassed(bosslevel->haspass);
		chg->set_reset_count(bosslevel->reset_count);
	}

	_boss_level_chg.clear();
}

bool player_t::can_consume(int32_t consumeid) {
	const tagProtoConsume* proto = MAProto(Consume, consumeid);
	if (NULL == proto)
		return false;

	for (int i = 0; i < ConsumeItemTypeNum; ++i) {
		int32_t item_protoid = proto->item_proto[i];
		int32_t item_num = proto->item_num[i];
		if (item_protoid < 0 || item_num <= 0)
			continue;

		if (!can_del_item(item_protoid, item_num))
			return false;
	}

	if (att(Pl8s_Money) < proto->money)
		return false;

	if (att(Pl8s_GameMoney) < proto->game_money)
		return false;

	if (att(Pl8s_Honor) < proto->honor)
		return false;

	if (att(Pl8s_Pearl) < proto->pearl)
		return false;

	return true;
}
void player_t::consume(int32_t consumeid) {
	const tagProtoConsume* proto = MAProto(Consume, consumeid);
	if (NULL == proto)
		return;

	for (int i = 0; i < ConsumeItemTypeNum; ++i) {
		int32_t item_protoid = proto->item_proto[i];
		int32_t item_num = proto->item_num[i];
		if (item_protoid < 0 || item_num <= 0)
			continue;

		del_item(item_protoid, item_num);
	}

	att(Pl8s_Money, att(Pl8s_Money) - proto->money);
	att(Pl8s_GameMoney, att(Pl8s_GameMoney) - proto->game_money);
	att(Pl8s_Honor, att(Pl8s_Honor) - proto->honor);
	att(Pl8s_Pearl, att(Pl8s_Pearl) - proto->pearl);
}

void player_t::reward(int32_t rewardid, OReward* reward, double pct, int32_t reward_pet) {
	const tagProtoReward* proto = MAProto(Reward, rewardid);
	if (NULL == proto)
		return;

	if (pct >= 1.0f) {
		pct = 1.0f;
		int32_t index = random(proto->one_item_prob, RewardOneItemTypeNum);
		if (index >= 0) {
			int32_t item_protoid = proto->one_item_proto[index];
			int32_t item_num = proto->one_item_num[index];
			if (item_protoid >= 0) {
				if (can_add_item(item_protoid, item_num)) {
					add_item(item_protoid, item_num);

					if (NULL != reward) {
						auto reward_item = reward->add_item();
						reward_item->set_proto(item_protoid);
						reward_item->set_num(item_num);
					}
				}
			}
		}

		for (int i = 0; i < RewardManyItemSlotNum; ++i) {
			int32_t index = random(proto->many_item_prob[i],
					RewardManyItemTypeNum);
			if (index >= 0) {
				int32_t item_protoid = proto->many_item_proto[i][index];
				int32_t item_num = proto->many_item_num[i][index];
				if (item_protoid >= 0) {
					if (can_add_item(item_protoid, item_num)) {
						add_item(item_protoid, item_num);

						if (NULL != reward) {
							auto reward_item = reward->add_item();
							reward_item->set_proto(item_protoid);
							reward_item->set_num(item_num);
						}
					}
				}
			}
		}

		if (reward_pet < 0) {
			for (int32_t i = 0; i < PlayerCombatSlotNum; ++i) {
				int32_t pet_serial = att(EPlAtt4Save(Pl4s_CombatBegin + i));
				if (pet_serial < 0)
					continue;

				auto pet = pets.get(pet_serial);
				if (NULL == pet)
					continue;

				double rate = 1.0f;
				if (att(Pl4s_ExpRateFinTime) >= g_clock->tt_now)
					rate += 1;
				if (g_clock->tm_now.tm_wday == 0 || g_clock->tm_now.tm_wday == 6)
					rate += g_game_cfg->WeekEndRewardExpRate;

				pet->add_exp(proto->pet_exp * rate);

				if (NULL != reward && i < PlayerCombatCSlotNum) {
					auto reward_pet = reward->add_pet();
					reward_pet->set_serial(pet->serial);
					reward_pet->set_exp(proto->pet_exp * rate);

					if (prob(proto->pet_friprob)) {
                        int fristage = pet->att(Pe4s_FStage);
                        const tagProtoPetFriendStage* fs_proto = ARProto(PetFriendStage, fristage);
                        if (proto) {
                            pet->att(Pe4s_FriVal, cut(pet->att(Pe4s_FriVal) + 1, 0, fs_proto->req_friendvalue));
                            reward_pet->set_fri(1);
                        }
					}
				}
			}
		} else {
			auto pet = pets.get(reward_pet);
			if (NULL != pet) {
				auto reward_pet = reward->add_pet();
				reward_pet->set_serial(pet->serial);

				pet->add_exp(proto->pet_exp);
				reward_pet->set_exp(proto->pet_exp);

				if (prob(proto->pet_friprob)) {
                    int fristage = pet->att(Pe4s_FStage);
                    const tagProtoPetFriendStage* fs_proto = ARProto(PetFriendStage, fristage);
                    if (proto) {
                        pet->att(Pe4s_FriVal, cut(pet->att(Pe4s_FriVal) + 1, 0, fs_proto->req_friendvalue));
                        reward_pet->set_fri(1);
                    }
				}
			}
		}
	}

	auto money = proto->money * pct;
	auto gamemoney = proto->game_money * pct;
	auto honor = proto->honor * pct;

	att(Pl8s_Money, att(Pl8s_Money) + money);
	att(Pl8s_GameMoney, att(Pl8s_GameMoney) + gamemoney);
	att(Pl8s_Honor, att(Pl8s_Honor) + honor);

	if (NULL != reward) {
		reward->set_money(money);
		reward->set_gamemoney(gamemoney);
		reward->set_honor(honor);
	}
}

void player_t::_init_petseen(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	petseen.clear();
	for (size_t i = 0; i < json.size(); ++i) {
		int32_t petid = json.get(i, 0).asUInt();
		petseen.insert(petid);
	}

	return;
}
void player_t::_save_petseen(Json::Value &json) {
	for (const auto &itr : petseen) {
		json.append(itr);
	}
}
void player_t::_destroy_petseen() {
	petseen_added.clear();
}
void player_t::_fill_petseen_init(cs::OPlayer* oplayer) {
	for (const auto &itr : petseen) {
		oplayer->add_petseen(itr);
	}
}
void player_t::_fill_petseen_sync(cs::SPlayerSync* splayersync) {
	while (!petseen_added.empty()) {
		splayersync->add_petseen_add(petseen_added.front());
		petseen_added.pop_front();
	}
}

void player_t::_init_pethas(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	pethas.clear();
	for (size_t i = 0; i < json.size(); ++i) {
		int32_t petid = json.get(i, 0).asUInt();
		pethas.insert(petid);
	}

	return;
}

void player_t::_save_pethas(Json::Value &json) {
	for (const auto &itr : pethas) {
		json.append(itr);
	}
}

void player_t::_destroy_pethas() {
	pethas_added.clear();
}

void player_t::_fill_pethas_init(cs::OPlayer* oplayer) {
	for (const auto &itr : pethas) {
		oplayer->add_pethas(itr);
	}
}

void player_t::_fill_pethas_sync(cs::SPlayerSync* splayersync) {
	while (!pethas_added.empty()) {
		splayersync->add_pethas_add(pethas_added.front());
		pethas_added.pop_front();
	}
}

void player_t::_init_skillhas(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	skillhas.clear();
	for (size_t i = 0; i < json.size(); ++i) {
		int32_t skillid = json.get(i, 0).asInt();
		skillhas.insert(skillid);
	}

	return;
}

void player_t::_save_skillhas(Json::Value &json) {
	for (const auto &itr : skillhas) {
		json.append(itr);
	}
}

void player_t::_destroy_skillhas() {
	skillhas_added.clear();
}

void player_t::_fill_skillhas_init(cs::OPlayer* oplayer) {
	for (const auto &itr : skillhas) {
		oplayer->add_skillhas(itr);
	}
}

void player_t::_fill_skillhas_sync(cs::SPlayerSync* splayersync) {
	while (!skillhas_added.empty()) {
		splayersync->add_skillhas_add(skillhas_added.front());
		skillhas_added.pop_front();
	}
}


void player_t::_init_levelhas(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	levelhas.clear();
	for (size_t i = 0; i < json.size(); ++i)
		levelhas.set_uint32(i, json.get(i, 0).asUInt());

	return;
}

void player_t::_save_levelhas(Json::Value &json) {
	for (int i = 0; i < levelhas.uint32_num(); ++i)
		json.append(levelhas.get_uint32(i));
}

void player_t::_destroy_levelhas() {
	levelhas_added.clear();
}

void player_t::_fill_levelhas_init(cs::OPlayer* oplayer) {
	for (int i = 0; i < levelhas.uint32_num(); ++i)
		oplayer->add_levelhas(levelhas.get_uint32(i));
}

void player_t::_fill_levelhas_sync(cs::SPlayerSync* splayersync) {
	while (!levelhas_added.empty()) {
		splayersync->add_levelhas_add(levelhas_added.front());
		levelhas_added.pop_front();
	}
}

void player_t::add_giftlog(int64_t sender, const string &to, const string &from, cs::EFriendGiftType type) {
	giftlog_t log;
	log.time = g_clock->tt_now;
	if (sender == id) {
		log.to = to;
	} else {
		log.from = from;
	}
	log.type = type;
	giftlog.push_back(log);
	giftlog_added.push_back(log);
	if (giftlog.size() > 6)
		giftlog.pop_front();
}


void player_t::_init_friends(const Json::Value &json) {
	if (!json.isObject())
		return;

	const Json::Value &friends_j = json["friends"];
	for (auto itr = friends_j.begin(); itr != friends_j.end(); ++itr)
		friends.insert((*itr).asInt64());

	const Json::Value &combated_j = json["combated"];
	for (auto itr = combated_j.begin(); itr != combated_j.end(); ++itr)
		friends_combated.insert((*itr).asInt64());

	const Json::Value &giftlog_j = json["giftlog"];
	for (auto itr = giftlog_j.begin(); itr != giftlog_j.end(); ++itr) {
		giftlog_t log;
		log.time = (*itr)["time"].asInt();
		log.to = (*itr)["to"].asCString();
		log.from = (*itr)["from"].asCString();
		log.type = (EFriendGiftType)(*itr)["type"].asInt();
		giftlog.push_back(log);
	}
}
void player_t::_save_friends(Json::Value &json) {
	Json::Value &friends_j = json["friends"];
	for (auto itr = friends.begin(); itr != friends.end(); ++itr)
		friends_j.append(Json::Int64(*itr));

	Json::Value &combated_j = json["combated"];
	for (auto itr = friends_combated.begin(); itr != friends_combated.end();++itr)
		combated_j.append(Json::Int64(*itr));

	Json::Value log;
	Json::Value &giftlog_j = json["giftlog"];
	for (auto itr = giftlog.begin(); itr != giftlog.end();++itr) {
		log["time"] = itr->time;
		log["to"] = itr->to;
		log["from"] = itr->from;
		log["type"] = itr->type;
		giftlog_j.append(log);
	}
}
void player_t::_destroy_friends() {
	friends.clear();
	friends_chged.clear();
	friends_combated.clear();
	friends_combated_added.clear();
	friends_combated_clear = false;
}
void player_t::_fill_friends_init(cs::OPlayer* oplayer) {
	for (auto itr = friends.begin(); itr != friends.end(); ++itr)
		oplayer->add_friends(*itr);

	for (auto itr = friends_combated.begin(); itr != friends_combated.end(); ++itr)
		oplayer->add_friend_combated(*itr);

	for (auto &itr : giftlog) {
		auto log = oplayer->add_giftlog();
		log->set_time(itr.time);
		log->set_type(itr.type);
		if (!itr.to.empty())
			log->set_to(itr.to);
		if (!itr.from.empty())
			log->set_from(itr.from);
	}
}
void player_t::_fill_friends_sync(cs::SPlayerSync* splayersync) {
	for (auto itr = friends_chged.begin(); itr != friends_chged.end(); ++itr) {
		if (friends.count(*itr) == 0) {
			splayersync->add_friend_remove(*itr);
		} else {
			splayersync->add_friend_add(*itr);
		}
	}
	friends_chged.clear();

	if (friends_combated_clear) {
		friends_combated.clear();
		friends_combated_added.clear();
		friends_combated_clear = false;
	} else {
		for (const auto &itr : friends_combated_added)
			splayersync->add_friend_combated_added(itr);
		friends_combated_added.clear();
	}

	for (auto &itr : giftlog_added) {
		auto log = splayersync->add_giftlog_added();
		log->set_time(itr.time);
		log->set_type(itr.type);
		if (!itr.to.empty())
			log->set_to(itr.to);
		if (!itr.from.empty())
			log->set_from(itr.from);
	}
	giftlog_added.clear();
}

bool player_t::is_in_combat(int32_t pet_serial) {
	for (int i = 0; i < PlayerCombatCSlotNum; ++i) {
		if (att(EPlAtt4Save(i + Pl4s_CombatBegin)) == pet_serial)
			return true;
	}
	return false;
}

void player_t::calc_equip_mod() {
	// 六个位置的属性影响
	static int xenhance_mod_att[PlayerEquipSlotNum][3] = { { 1, EPA_Hp, -1 }, {
			2, EPA_Hp, EPA_Defend }, { 1, EPA_Defend, -1 },
			{ 1, EPA_Attack, -1 }, { 1, EPA_Speed, -1 }, { 2, EPA_Attack,
					EPA_Speed }, };

	// 宠物属性影响因数
	static int32_t xenhance_att_factor[2][CanSeePetAttNum] = { { 5, 1, 1, 1 }, // 1个属性
			{ 10, 2, 2, 2 },  // 2个属性
			};

	// 洗星属性影响因数
	static int32_t xstar_att_factor[CanSeePetAttNum] = { 5, 1, 1, 1, };

	int mods[CanSeePetAttNum] = { 0 };

	for (int i = 0; i < PlayerEquipSlotNum; ++i) {
		const equip_t &equip = equip_bar[i];
		const tagProtoEquipLevel* equip_proto =
				ARProto(EquipLevel, equip.equip_level);
		if (NULL == equip_proto)
			continue;

		int att_num = xenhance_mod_att[i][0];

		for (int j = 0; j < att_num; ++j) {
			int att = xenhance_mod_att[i][1 + j];
			int32_t val = xenhance_att_factor[att_num][att]
					* (equip_proto->enhance_base_num + equip.enhance_num);

			if (!EPetAtt_IsValid(att))
				continue;

			mods[att] += val;
		}

		for (int j = 0; j < CanSeePetAttNum; ++j) {
			mods[j] += xstar_att_factor[j] * equip.star_num[j];
		}
	}

	for (int j = 0; j < CanSeePetAttNum; ++j) {
		att(EPlAtt4Save(j + Pl4s_EquipAttBegin), mods[j]);
	}
}

void player_t::calc_total_combat_force() {
	int32_t cf = att(Pl4s_CombatForce);
	for (int i = 0; i < PlayerCombatCSlotNum; ++i) {
		int32_t serial = att(EPlAtt4Save(i + Pl4s_CombatBegin));
		pet_t* pet = pets.get(serial);
		if (NULL != pet) {
			cf += pet->att(Pe4s_CombatForce);
		}
	}
	if (cur_sprite >= 0) {
		auto sprite = sprites.get(cur_sprite);
		if (sprite) {
			cf += sprite->calc_combat_force();
		}
	}

	att(Pl4s_TotalCombatForce, cf);

	auto info = g_playermgr->infos.get(id);
	if (NULL != info)
		info->total_combat_force = cf;
}

void player_t::calc_combat_force() {
	int cf = att(Pl4s_EquipAtt1) / 5 + att(Pl4s_EquipAtt2) + att(Pl4s_EquipAtt3)
			+ att(Pl4s_EquipAtt4);
	cf *= 3;
	att(Pl4s_CombatForce, cf);
}

void player_t::_init_arenalogs(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	for (auto itr = json.begin(); itr != json.end(); ++itr) {
		arenalog_t log;
		log.attacker = (*itr)["attacker"].asCString();
		log.defender = (*itr)["defender"].asCString();
		log.win = (*itr)["win"].asBool();
		log.index = (*itr)["index"].asInt();

		arenalog.emplace_back(log);
	}
}
void player_t::_save_arenalogs(Json::Value &json) {
	for (auto itr = arenalog.begin(); itr != arenalog.end(); ++itr) {
		Json::Value one;
		one["attacker"] = itr->attacker;
		one["defender"] = itr->defender;
		one["win"] = itr->win;
		one["index"] = itr->index;
		json.append(one);
	}
}
void player_t::_destroy_arenalogs() {
	arenalog.clear();
}

void player_t::_init_alives(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	for (auto itr = json.begin(); itr != json.end(); ++itr)
		alives.insert((*itr).asInt());

}
void player_t::_save_alives(Json::Value &json) {
	for (auto itr = alives.begin(); itr != alives.end(); ++itr)
		json.append(Json::Int64(*itr));
}
void player_t::_destroy_alives() {
	alives.clear();
}

void player_t::_init_train(const Json::Value &json) {
	if (!json.isArray() && !json.isNull())
		return;

	assert(json.size() <= MaxTrainRoomNum);
	for (int i=0; i<json.size(); ++i) {
		const auto &room = json.get(i, Json::nullValue);
		if (room.isNull())
			break;

		_train[i].type = room["type"].asInt();
		_train[i].lvl = room["lvl"].asInt();
		_train[i].finish = room["finish"].asInt();
		_train[i].petid = room["petid"].asInt();
		_train[i].petlvl = room["petlvl"].asInt();
	}
}
void player_t::_save_train(Json::Value &json) {
    Json::Value room;
	for (int i=0; i<MaxTrainRoomNum; ++i) {
		room["type"] = _train[i].type;
		room["lvl"] = _train[i].lvl;
		room["finish"] = _train[i].finish;
		room["petid"] = _train[i].petid;
		room["petlvl"] = _train[i].petlvl;
		json.append(room);
	}
	assert(json.size() == MaxTrainRoomNum);
}
void player_t::_destroy_train() {
	for (int i=0; i<MaxTrainRoomNum; ++i) {
		_train[i].type = -1;
		_train[i].lvl = 0;
		_train[i].finish = -1;
		_train[i].petid = -1;
		_train[i].petlvl = 0;
	}
}
void player_t::_fill_train_init(cs::OPlayer* oplayer) {
	for (int i=0; i<MaxTrainRoomNum; ++i) {
		auto train = oplayer->add_train();
		train->set_index(i);
		train->set_lvl(_train[i].lvl);
		if (_train[i].finish >= 0) {
			train->set_type(_train[i].type);
			train->set_finish(_train[i].finish);
			train->set_petid(_train[i].petid);
			train->set_petlvl(_train[i].petlvl);
		}
	}
}
void player_t::_fill_train_sync(cs::SPlayerSync* splayersync) {
	for (auto &itr : _train_mods) {
		if (itr < 0 || itr >= MaxTrainRoomNum)
			continue;

		auto train = splayersync->add_train();
		train->set_index(itr);
		train->set_type(_train[itr].type);
		train->set_lvl(_train[itr].lvl);
		train->set_finish(_train[itr].finish);
		train->set_petid(_train[itr].petid);
		train->set_petlvl(_train[itr].petlvl);
	}
	_train_mods.clear();
}


bool test_player() {
	player_t newpd;
	newpd.init(1, "killerg", EPS_Male, "haha");

	OPlayer init;
	newpd.fill_oplayer(&init);

	newpd.add_item(3002, 1);
	newpd.add_item(3002, 1);
	newpd.add_item(2001, 1);
	newpd.add_item(2002, 1);
	newpd.add_item(2001, 100);
	newpd.add_item(2002, 100);
	auto pp = new pet_t;
	if (!pp->init(int32_t(101), newpd.n.valid_pet_serial++)) {
		assert(0);
		return false;
	}
	newpd.add_pet(pp);

	pp = new pet_t;
	if (!pp->init(int32_t(102), newpd.n.valid_pet_serial++)) {
		assert(0);
		return false;
	}
	newpd.add_pet(pp);

	SPlayerSync sync2;
	newpd.fill_splayersync(&sync2);

	sync2.Clear();
	newpd.fill_splayersync(&sync2);

	newpd.del_item(2001, 100);
	newpd.del_item(3002, 3);

	SPlayerSync sync3;
	newpd.fill_splayersync(&sync3);

	auto sp = new sprite_t;
	if (!sp->init(0, newpd.n.valid_sprite_serial++)) {
		assert(0);
		return false;
	}
	newpd.add_sprite(sp);

	Json::Value json;
	newpd.save(json);

	string a1 = json.toStyledString();
	//printf("%s", a1.c_str());

	player_t loaded;
	if (!loaded.init(json)) {
		assert(0);
		return false;
	}

	if (loaded.need_sync_player()) {
		SPlayerSync sync;
		loaded.fill_splayersync(&sync);
	}

	Json::Value json2;
	loaded.save(json2);

	string a2 = json2.toStyledString();
	//printf("%s", a2.c_str());

	assert(a1 == a2);

	return true;
}

#include "datasave.h"

void fill_sprite(sprite_t* sprite, datasave_sprite_t* save) {
	save->proto = sprite->proto->id;
	save->serial = sprite->serial;
	for (int i=0; i<SpriteSkillNum; ++i)
		save->skill_level[i] = sprite->skill_level[i];
}

void fill_pet(pet_t* pet, datasave_pet_t* save) {
	save->serial = pet->serial;

	for (int i=0; i<EPeAtt8Save_ARRAYSIZE; ++i) {
		save->pe8s[i] = pet->s8.data[i];
	}

	for (int i=0; i<EPeAtt4Save_ARRAYSIZE; ++i) {
		save->pe4s[i] = pet->s4.data[i];
	}

	for (int i=0; i<EPeAttDSave_ARRAYSIZE; ++i) {
		save->peds[i] = pet->sd.data[i];
	}

	for (int i=0; i<EPeAttSSave_ARRAYSIZE; ++i) {
		strncpy(save->pess[i], pet->ss.data[i].c_str(), ShortStringLen);
	}

}

void fill_player(player_t* player, datasave_player_t* save) {
	memset(save, -1, sizeof(*save));

	save->id = player->id;
	save->create = player->create;
	strncpy(save->account, player->account.c_str(), ShortStringLen);
	save->valid_pet_serial = player->n.valid_pet_serial;
	save->valid_item_serial = player->n.valid_item_serial;
	save->valid_sprite_serial = player->n.valid_sprite_serial;
	save->code = player->n.code;
	save->code_trytime = player->n.code_trytime;
	save->logoff_date = player->n.logoff_date;
	save->week_refresh_time = player->n.week_refresh_time;
	save->compensate_gain_stamp = player->n.compensate_gain_stamp;
	save->forbid = player->n.forbid;

	int index = 0;
	for (auto &itr : player->bag) {
		if (index >= PlayerMaxBagPageNum * PlayerBagSlotNumPerPage)
			break;

		auto item = save->items + index++;
		item->serial = itr.second->serial;
		item->proto = itr.second->proto->id;
		item->num = itr.second->num;
	}

	index = 0;
	for (auto &itr : player->pets) {
		if (index >= PlayerMaxPetNum)
			break;

		fill_pet(itr.second, save->pets + index++);
	}

	index = 0;
	for (auto &itr : player->sprites) {
		if (index >= PlayerMaxSpriteNum)
			break;

		fill_sprite(itr.second, save->sprites + index++);
	}

	for (int i=0; i<PlayerEquipSlotNum; ++i) {
		auto equip = player->equip_bar + i;
		auto equipsave = save->equips + i;

		equipsave->enhance = equip->enhance_num;
		equipsave->level = equip->equip_level;
		for (int ii=0; ii<CanSeePetAttNum; ++ii)
			equipsave->star_num[ii] = equip->star_num[ii];
	}

	for (int i=0; i<EPlAtt8Save_ARRAYSIZE; ++i) {
		save->pl8s[i] = player->s8.data[i];
	}

	for (int i=0; i<EPlAtt4Save_ARRAYSIZE; ++i) {
		save->pl4s[i] = player->s4.data[i];
	}

	for (int i=0; i<EPlAttDSave_ARRAYSIZE; ++i) {
		save->plds[i] = player->sd.data[i];
	}

	for (int i=0; i<EPlAttSSave_ARRAYSIZE; ++i) {
		strncpy(save->plss[i], player->ss.data[i].c_str(), ShortStringLen);
	}

	index = 0;
	for (auto &itr : player->_boss_level_progress) {
		if (index >= MaxLevelNum / 6)
			break;

		auto bosslevel = itr.second;
		auto bosslevelsave = save->bosslevels + index++;

		bosslevelsave->id = bosslevel->id;
		bosslevelsave->cur_pass = bosslevel->cur_pass;
		bosslevelsave->haspass = bosslevel->haspass;
		bosslevelsave->reset_count = bosslevel->reset_count;
	}

	index = 0;
	for (auto &itr : player->petseen) {
		if (index >= 120*3)
			break;

		save->pet_seen[index++] = itr;
	}

	index = 0;
	for (auto &itr : player->pethas) {
		if (index >= 120*3)
			break;

		save->pet_has[index++] = itr;
	}

	for (int i=0; i<MaxLevelNum / 32 + 1; ++i) {
		save->level_has[i] = player->levelhas.get_uint32(i);
	}

	index = 0;
	for (auto &itr : player->friends) {
		if (index >= MaxFriendNum)
			break;

		save->friends[index++] = itr;
	}

	index = 0;
	for (auto &itr : player->friends_combated) {
		if (index >= MaxFriendNum)
			break;

		save->friends_combat[index++] = itr;
	}

	index = 0;
	for (auto &itr : player->giftlog) {
		if (index >= 6)
			break;

		auto giftlogsave = save->giftlogs + index++;
		giftlogsave->time = itr.time;
		giftlogsave->type = itr.type;
		strncpy(giftlogsave->to, itr.to.c_str(), ShortStringLen);
		strncpy(giftlogsave->from, itr.from.c_str(), ShortStringLen);
	}

	index = 0;
	for (auto &itr : player->alives) {
		if (index >= 8)
			break;

		save->alives[index++] = itr;
	}

	for (int i=0; i<MaxTrainRoomNum; ++i) {
		auto train = player->_train + i;
		auto trainsave = save->trains + i;

		trainsave->finish = train->finish;
		trainsave->lvl = train->lvl;
		trainsave->petid = train->petid;
		trainsave->petlvl = train->petlvl;
		trainsave->type = train->type;
	}

}

void pet_to_json(datasave_pet_t* save, Json::Value &json) {

}

void sprite_to_json(datasave_sprite_t* save, Json::Value &json) {

}
//
//void player_to_json(datasave_player_t* save, Json::Value &json) {
//	json["objtype"] = "player";
//	json["serverid"] = save->id / co::MaxAccountNum;
//	json["id"] = save->id % co::MaxAccountNum;
//	json["create"] = save->create;
//	json["account"] = save->account;
//	json["code"] = save->code;
//	json["logoff_date"] = save->logoff_date;
//	json["week_refresh_time"] = save->week_refresh_time;
//	json["valid_item_serial"] = save->valid_item_serial;
//	json["valid_pet_serial"] = save->valid_pet_serial;
//	json["valid_sprite_serial"] = save->valid_sprite_serial;
//	json["code_trytime"] = save->code_trytime;
//	json["forbid"] = save->forbid;
//	json["compensate_gain_stamp"] = save->compensate_gain_stamp;
//
//	for (int i=0; i<PlayerMaxPetNum; ++i) {
//		if (save->pets[i].serial < 0)
//			break;
//
//		Json::Value json_pet;
//		pet_to_json(save->pets + i, json_pet);
//		json["pets"].append(json_pet);
//	}
//
//	for (int i=0; i<PlayerMaxSpriteNum; ++i) {
//		if (save->sprites[i].serial < 0)
//			break;
//
//		Json::Value json_sprite;
//		sprite_to_json(save->sprites + i, json_sprite);
//		json["sprites"].append(json_sprite);
//	}
//
//	for (int i=0; i<PlayerMaxBagPageNum * PlayerBagSlotNumPerPage; ++i) {
//		auto itemsave = save->items + i;
//		if (itemsave->serial < 0)
//			break;
//
//		Json::Value json_item;
//		json_item["serial"] = itemsave->serial;
//		json_item["proto"] = itemsave->proto;
//		json_item["num"] = itemsave->num;
//		json["bag"].append(json_item);
//	}
//
//
//	for (int i=0; i<PlayerEquipSlotNum; ++i) {
//		auto equipsave = save->equips + i;
//
//		Json::Value json_equip;
//		json_equip["level"] = equipsave->level;
//		json_equip["enhance"] = equipsave->enhance;
//		Json::Value &json_star = json_equip["star"];
//		for (int ii=0; ii<CanSeePetAttNum; ++ii) {
//			json_star.append(equipsave->star_num[ii]);
//		}
//
//		json["equip_bar"].append(json_equip);
//	}
//
//	Json::Value &json_syncatt = json["syncatt"];
//
//	Json::Value &json_n8 = json_syncatt["n8"];
//	for (int i=0; i<EPlAtt8Save_ARRAYSIZE; ++i) {
//		json_n8.append(Json::Int64(save->pl8s[i]));
//	}
//
//	Json::Value &json_n4 = json_syncatt["n4"];
//	for (int i=0; i<EPlAtt4Save_ARRAYSIZE; ++i) {
//		json_n4.append(save->pl4s[i]);
//	}
//
//	Json::Value &json_d = json_syncatt["d"];
//	for (int i=0; i<EPlAttDSave_ARRAYSIZE; ++i) {
//		json_d.append(save->plds[i]);
//	}
//
//	Json::Value &json_str = json_syncatt["str"];
//	for (int i=0; i<EPlAttSSave_ARRAYSIZE; ++i) {
//		json_str.append(save->plss[i]);
//	}
//
//	for (int i=0; i<MaxLevelNum / 6; ++i) {
//		auto progresssave = save->bosslevels + i;
//		if (progresssave->id < 0)
//			break;
//
//		Json::Value json_progress;
//		json_progress["id"] = progresssave->id;
//		json_progress["cur_pass"] = progresssave->cur_pass;
//		json_progress["haspass"] = progresssave->haspass;
//		json_progress["reset_count"] = progresssave->reset_count;
//
//		json["bosslevel_progress"].append(json_progress);
//	}
//
//	for (int i=0; i<120 * 3; ++i) {
//		if (save->pet_seen[i] < 0)
//			break;
//
//		json["petseen"].append(save->pet_seen[i]);
//	}
//
//	for (int i=0; i<120 * 3; ++i) {
//		if (save->pet_has[i] < 0)
//			break;
//
//		json["pethas"].append(save->pet_has[i]);
//	}
//
//	for (int i=0; i<MaxLevelNum/32+1; ++i) {
//		json["levelhas"].append(save->level_has[i]);
//	}
//
//	for (int i=0; i<MaxFriendNum; ++i) {
//		if (save->friends[i] < 0)
//			break;
//
//		json["friends"].append(Json::Int64(save->friends[i]));
//	}
//
//	for (int i=0; i<save->account) {
//		Json::Value one;
//		one["attacker"] = itr->attacker;
//		one["defender"] = itr->defender;
//		one["win"] = itr->win;
//		one["index"] = itr->index;
//		json.append(one);
//	}
//
//
//}

bool test_save() {
	string path = fmt_cstr("%s/bin/player_1_41401_new", get_deploy());
	string data = load_file(path.c_str());
	Json::Value json = to_json(data);
	assert(!json.isNull());

	player_t player;
	assert(player.init(json));
	double begin = my_clock_t::get_time();
	for (int i=0; i<1000; ++i) {
		Json::Value json;
		player.save(json);
	}
	printf("%d %f\n", data.size() * 8000, my_clock_t::get_time() - begin);

	begin = my_clock_t::get_time();
	for (int i=0; i<1000; ++i) {
		datasave_player_t playersave;
		fill_player(&player, &playersave);
	}
	printf("%d %f\n", sizeof(datasave_player_t) * 8000, my_clock_t::get_time() - begin);

	datasave_player_t playersave;
	fill_player(&player, &playersave);
	begin = my_clock_t::get_time();
	for (int i=0; i<1000; ++i) {
		Json::Value json;
		//player_to_json(&playersave, json);
	}
	printf("%d %f\n", sizeof(datasave_player_t) * 8000, my_clock_t::get_time() - begin);

	return true;
}
